using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.Core.Net;

namespace HslCommunication.Robot.YASKAWA
{
	/// <summary>
	/// 安川机器人的Ethernet 服务器功能的通讯类<br />
	/// Yaskawa robot's Ethernet server features a communication class
	/// </summary>
	public class YRC1000TcpNet : NetworkDoubleBase, IRobotNet
	{
		/// <summary>
		/// 指定机器人的ip地址及端口号来实例化对象<br />
		/// Specify the robot's IP address and port number to instantiate the object
		/// </summary>
		/// <param name="ipAddress">Ip地址</param>
		/// <param name="port">端口号</param>
		public YRC1000TcpNet(string ipAddress, int port)
		{
			IpAddress = ipAddress;
			Port = port;
			base.ByteTransform = new ReverseWordTransform();
		}

		/// <inheritdoc cref="M:HslCommunication.Core.Net.IRobotNet.Read(System.String)" />
		public OperateResult<byte[]> Read(string address)
		{
			OperateResult<string> operateResult = ReadString(address);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			return OperateResult.CreateSuccessResult(Encoding.ASCII.GetBytes(operateResult.Content));
		}

		/// <inheritdoc cref="M:HslCommunication.Core.Net.IRobotNet.ReadString(System.String)" />
		public OperateResult<string> ReadString(string address)
		{
			if (address.IndexOf('.') >= 0 || address.IndexOf(':') >= 0 || address.IndexOf(';') >= 0)
			{
				string[] array = address.Split('.', ':', ';');
				return ReadByCommand(array[0], array[1]);
			}
			return ReadByCommand(address, null);
		}

		/// <inheritdoc cref="M:HslCommunication.Core.Net.IRobotNet.Write(System.String,System.Byte[])" />
		public OperateResult Write(string address, byte[] value)
		{
			return Write(address, Encoding.ASCII.GetString(value));
		}

		/// <inheritdoc cref="M:HslCommunication.Core.Net.IRobotNet.Write(System.String,System.String)" />
		public OperateResult Write(string address, string value)
		{
			return ReadByCommand(address, value);
		}

		/// <inheritdoc cref="M:HslCommunication.Core.Net.IRobotNet.ReadAsync(System.String)" />
		public async Task<OperateResult<byte[]>> ReadAsync(string address)
		{
			OperateResult<string> read = await ReadStringAsync(address);
			if (!read.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(read);
			}
			return OperateResult.CreateSuccessResult(Encoding.ASCII.GetBytes(read.Content));
		}

		/// <inheritdoc cref="M:HslCommunication.Core.Net.IRobotNet.ReadStringAsync(System.String)" />
		public async Task<OperateResult<string>> ReadStringAsync(string address)
		{
			if (address.IndexOf('.') >= 0 || address.IndexOf(':') >= 0 || address.IndexOf(';') >= 0)
			{
				string[] commands = address.Split('.', ':', ';');
				return await ReadByCommandAsync(commands[0], commands[1]);
			}
			return await ReadByCommandAsync(address, null);
		}

		/// <inheritdoc cref="M:HslCommunication.Core.Net.IRobotNet.WriteAsync(System.String,System.Byte[])" />
		public async Task<OperateResult> WriteAsync(string address, byte[] value)
		{
			return await WriteAsync(address, Encoding.ASCII.GetString(value));
		}

		/// <inheritdoc cref="M:HslCommunication.Core.Net.IRobotNet.WriteAsync(System.String,System.String)" />
		public async Task<OperateResult> WriteAsync(string address, string value)
		{
			return await ReadByCommandAsync(address, value);
		}

		/// <summary>
		/// before read data , the connection should be Initialized
		/// </summary>
		/// <param name="socket">connected socket</param>
		/// <returns>whether is the Initialization is success.</returns>
		protected override OperateResult InitializationOnConnect(Socket socket)
		{
			OperateResult<string> operateResult = ReadFromCoreServer(socket, "CONNECT Robot_access KeepAlive:-1\r\n");
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			if (operateResult.Content == "OK:YR Information Server(Ver) Keep-Alive:-1.\r\n")
			{
				return OperateResult.CreateSuccessResult();
			}
			if (!operateResult.Content.StartsWith("OK:"))
			{
				return new OperateResult(operateResult.Content);
			}
			isPersistentConn = false;
			return OperateResult.CreateSuccessResult();
		}

		/// <summary>
		/// before read data , the connection should be Initialized
		/// </summary>
		/// <param name="socket">connected socket</param>
		/// <returns>whether is the Initialization is success.</returns>
		protected override async Task<OperateResult> InitializationOnConnectAsync(Socket socket)
		{
			OperateResult<string> read = await ReadFromCoreServerAsync(socket, "CONNECT Robot_access KeepAlive:-1\r\n");
			if (!read.IsSuccess)
			{
				return read;
			}
			if (read.Content == "OK:YR Information Server(Ver) Keep-Alive:-1.\r\n")
			{
				return OperateResult.CreateSuccessResult();
			}
			if (!read.Content.StartsWith("OK:"))
			{
				return new OperateResult(read.Content);
			}
			isPersistentConn = false;
			return OperateResult.CreateSuccessResult();
		}

		/// <summary>
		/// 重写父类的数据交互方法，接收的时候采用标识符来接收<br />
		/// Overrides the data interaction method of the parent class, using an identifier when receiving
		/// </summary>
		/// <param name="socket">套接字</param>
		/// <param name="send">发送的数据</param>
		/// <returns>发送结果对象</returns>
		public override OperateResult<byte[]> ReadFromCoreServer(Socket socket, byte[] send)
		{
			base.LogNet?.WriteDebug(ToString(), StringResources.Language.Send + " : " + SoftBasic.ByteToHexString(send, ' '));
			OperateResult operateResult = Send(socket, send);
			if (!operateResult.IsSuccess)
			{
				socket?.Close();
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			if (base.ReceiveTimeOut < 0)
			{
				return OperateResult.CreateSuccessResult(new byte[0]);
			}
			OperateResult<byte[]> operateResult2 = ReceiveCommandLineFromSocket(socket, 13, 10, base.ReceiveTimeOut);
			if (!operateResult2.IsSuccess)
			{
				return new OperateResult<byte[]>(StringResources.Language.ReceiveDataTimeout + base.ReceiveTimeOut);
			}
			base.LogNet?.WriteDebug(ToString(), StringResources.Language.Receive + " : " + SoftBasic.ByteToHexString(operateResult2.Content, ' '));
			return OperateResult.CreateSuccessResult(operateResult2.Content);
		}

		/// <summary>
		/// Read string value from socket
		/// </summary>
		/// <param name="socket">connected socket</param>
		/// <param name="send">string value</param>
		/// <returns>received string value with is successfully</returns>
		protected OperateResult<string> ReadFromCoreServer(Socket socket, string send)
		{
			OperateResult<byte[]> operateResult = ReadFromCoreServer(socket, Encoding.Default.GetBytes(send));
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<string>(operateResult);
			}
			return OperateResult.CreateSuccessResult(Encoding.Default.GetString(operateResult.Content));
		}

		/// <summary>
		/// 根据指令来读取设备的信息，如果命令数据为空，则传入null即可，注意，所有的命令不带换行符<br />
		/// Read the device information according to the instructions. If the command data is empty, pass in null. Note that all commands do not have a newline character
		/// </summary>
		/// <param name="command">命令的内容</param>
		/// <param name="commandData">命令数据内容</param>
		/// <returns>最终的结果内容，需要对IsSuccess进行验证</returns>
		public OperateResult<string> ReadByCommand(string command, string commandData)
		{
			InteractiveLock.Enter();
			OperateResult<Socket> availableSocket = GetAvailableSocket();
			if (!availableSocket.IsSuccess)
			{
				IsSocketError = true;
				base.AlienSession?.Offline();
				InteractiveLock.Leave();
				return OperateResult.CreateFailedResult<string>(availableSocket);
			}
			string send = string.IsNullOrEmpty(commandData) ? ("HOSTCTRL_REQUEST " + command + " 0\r\n") : $"HOSTCTRL_REQUEST {command} {commandData.Length + 1}\r\n";
			OperateResult<string> operateResult = ReadFromCoreServer(availableSocket.Content, send);
			if (!operateResult.IsSuccess)
			{
				IsSocketError = true;
				base.AlienSession?.Offline();
				InteractiveLock.Leave();
				return OperateResult.CreateFailedResult<string>(operateResult);
			}
			if (!operateResult.Content.StartsWith("OK:"))
			{
				if (!isPersistentConn)
				{
					availableSocket.Content?.Close();
				}
				InteractiveLock.Leave();
				return new OperateResult<string>(operateResult.Content.Remove(operateResult.Content.Length - 2));
			}
			if (!string.IsNullOrEmpty(commandData))
			{
				byte[] bytes = Encoding.ASCII.GetBytes(commandData + "\r");
				base.LogNet?.WriteDebug(ToString(), StringResources.Language.Send + " : " + SoftBasic.ByteToHexString(bytes, ' '));
				OperateResult operateResult2 = Send(availableSocket.Content, bytes);
				if (!operateResult2.IsSuccess)
				{
					availableSocket.Content?.Close();
					IsSocketError = true;
					base.AlienSession?.Offline();
					InteractiveLock.Leave();
					return OperateResult.CreateFailedResult<string>(operateResult2);
				}
			}
			OperateResult<byte[]> operateResult3 = ReceiveCommandLineFromSocket(availableSocket.Content, 13, base.ReceiveTimeOut);
			if (!operateResult3.IsSuccess)
			{
				IsSocketError = true;
				base.AlienSession?.Offline();
				InteractiveLock.Leave();
				return OperateResult.CreateFailedResult<string>(operateResult3);
			}
			string @string = Encoding.ASCII.GetString(operateResult3.Content);
			if (@string.StartsWith("ERROR:"))
			{
				if (!isPersistentConn)
				{
					availableSocket.Content?.Close();
				}
				InteractiveLock.Leave();
				Receive(availableSocket.Content, 1);
				return new OperateResult<string>(@string);
			}
			if (@string.StartsWith("0000\r"))
			{
				if (!isPersistentConn)
				{
					availableSocket.Content?.Close();
				}
				Receive(availableSocket.Content, 1);
				InteractiveLock.Leave();
				return OperateResult.CreateSuccessResult("0000");
			}
			if (!isPersistentConn)
			{
				availableSocket.Content?.Close();
			}
			InteractiveLock.Leave();
			return OperateResult.CreateSuccessResult(@string.Remove(@string.Length - 1));
		}

		/// <inheritdoc cref="M:HslCommunication.Robot.YASKAWA.YRC1000TcpNet.ReadFromCoreServer(System.Net.Sockets.Socket,System.Byte[])" />
		public override async Task<OperateResult<byte[]>> ReadFromCoreServerAsync(Socket socket, byte[] send)
		{
			base.LogNet?.WriteDebug(ToString(), StringResources.Language.Send + " : " + SoftBasic.ByteToHexString(send, ' '));
			OperateResult sendResult = await SendAsync(socket, send);
			if (!sendResult.IsSuccess)
			{
				socket?.Close();
				return OperateResult.CreateFailedResult<byte[]>(sendResult);
			}
			if (base.ReceiveTimeOut < 0)
			{
				return OperateResult.CreateSuccessResult(new byte[0]);
			}
			OperateResult<byte[]> resultReceive = await ReceiveCommandLineFromSocketAsync(socket, 13, 10, base.ReceiveTimeOut);
			if (!resultReceive.IsSuccess)
			{
				return new OperateResult<byte[]>(StringResources.Language.ReceiveDataTimeout + base.ReceiveTimeOut);
			}
			base.LogNet?.WriteDebug(ToString(), StringResources.Language.Receive + " : " + SoftBasic.ByteToHexString(resultReceive.Content, ' '));
			return OperateResult.CreateSuccessResult(resultReceive.Content);
		}

		/// <inheritdoc cref="M:HslCommunication.Robot.YASKAWA.YRC1000TcpNet.ReadFromCoreServer(System.Net.Sockets.Socket,System.String)" />
		protected async Task<OperateResult<string>> ReadFromCoreServerAsync(Socket socket, string send)
		{
			OperateResult<byte[]> read = await ReadFromCoreServerAsync(socket, Encoding.Default.GetBytes(send));
			if (!read.IsSuccess)
			{
				return OperateResult.CreateFailedResult<string>(read);
			}
			return OperateResult.CreateSuccessResult(Encoding.Default.GetString(read.Content));
		}

		/// <inheritdoc cref="M:HslCommunication.Robot.YASKAWA.YRC1000TcpNet.ReadByCommand(System.String,System.String)" />
		public async Task<OperateResult<string>> ReadByCommandAsync(string command, string commandData)
		{
			InteractiveLock.Enter();
			OperateResult<Socket> resultSocket = await GetAvailableSocketAsync();
			if (!resultSocket.IsSuccess)
			{
				IsSocketError = true;
				base.AlienSession?.Offline();
				InteractiveLock.Leave();
				return OperateResult.CreateFailedResult<string>(resultSocket);
			}
			OperateResult<string> readCommand = await ReadFromCoreServerAsync(send: string.IsNullOrEmpty(commandData) ? ("HOSTCTRL_REQUEST " + command + " 0\r\n") : $"HOSTCTRL_REQUEST {command} {commandData.Length + 1}\r\n", socket: resultSocket.Content);
			if (!readCommand.IsSuccess)
			{
				IsSocketError = true;
				base.AlienSession?.Offline();
				InteractiveLock.Leave();
				return OperateResult.CreateFailedResult<string>(readCommand);
			}
			if (!readCommand.Content.StartsWith("OK:"))
			{
				if (!isPersistentConn)
				{
					resultSocket.Content?.Close();
				}
				InteractiveLock.Leave();
				return new OperateResult<string>(readCommand.Content.Remove(readCommand.Content.Length - 2));
			}
			if (!string.IsNullOrEmpty(commandData))
			{
				byte[] send2 = Encoding.ASCII.GetBytes(commandData + "\r");
				base.LogNet?.WriteDebug(ToString(), StringResources.Language.Send + " : " + SoftBasic.ByteToHexString(send2, ' '));
				OperateResult sendResult2 = await SendAsync(resultSocket.Content, send2);
				if (!sendResult2.IsSuccess)
				{
					resultSocket.Content?.Close();
					IsSocketError = true;
					base.AlienSession?.Offline();
					InteractiveLock.Leave();
					return OperateResult.CreateFailedResult<string>(sendResult2);
				}
			}
			OperateResult<byte[]> resultReceive2 = await ReceiveCommandLineFromSocketAsync(resultSocket.Content, 13, base.ReceiveTimeOut);
			if (!resultReceive2.IsSuccess)
			{
				IsSocketError = true;
				base.AlienSession?.Offline();
				InteractiveLock.Leave();
				return OperateResult.CreateFailedResult<string>(resultReceive2);
			}
			string commandDataReturn = Encoding.ASCII.GetString(resultReceive2.Content);
			if (commandDataReturn.StartsWith("ERROR:"))
			{
				if (!isPersistentConn)
				{
					resultSocket.Content?.Close();
				}
				InteractiveLock.Leave();
				await ReceiveAsync(resultSocket.Content, 1);
				return new OperateResult<string>(commandDataReturn);
			}
			if (commandDataReturn.StartsWith("0000\r"))
			{
				if (!isPersistentConn)
				{
					resultSocket.Content?.Close();
				}
				await ReceiveAsync(resultSocket.Content, 1);
				InteractiveLock.Leave();
				return OperateResult.CreateSuccessResult("0000");
			}
			if (!isPersistentConn)
			{
				resultSocket.Content?.Close();
			}
			InteractiveLock.Leave();
			return OperateResult.CreateSuccessResult(commandDataReturn.Remove(commandDataReturn.Length - 1));
		}

		/// <summary>
		/// 读取机器人的报警信息
		/// </summary>
		/// <returns>原始的报警信息</returns>
		public OperateResult<string> ReadRALARM()
		{
			return ReadByCommand("RALARM", null);
		}

		/// <summary>
		/// 读取机器人的坐标数据信息
		/// </summary>
		/// <returns>原始的报警信息</returns>
		public OperateResult<string> ReadRPOSJ()
		{
			return ReadByCommand("RPOSJ", null);
		}

		/// <inheritdoc cref="M:HslCommunication.Robot.YASKAWA.YRC1000TcpNet.ReadRALARM" />
		public async Task<OperateResult<string>> ReadRALARMAsync()
		{
			return await ReadByCommandAsync("RALARM", null);
		}

		/// <inheritdoc cref="M:HslCommunication.Robot.YASKAWA.YRC1000TcpNet.ReadRPOSJ" />
		public async Task<OperateResult<string>> ReadRPOSJAsync()
		{
			return await ReadByCommandAsync("RPOSJ", null);
		}

		/// <inheritdoc />
		public override string ToString()
		{
			return $"YRC1000TcpNet Robot[{IpAddress}:{Port}]";
		}
	}
}
